#!/bin/sh
##### This is my (demuredemeanor) borgbackup wrapper script.
# Blog Post: https://demu.red/blog/2017/02/backups-revisited/
# Source Code:  https://notabug.org/demure/scripts/src/master/borg_wrap.sh
# Uses tabstop=4; shiftwidth=4 tabs; foldmarker={{{,}}};
#
# This is a script to run my borg backups.
# It has basic config error checking, and verifies I am on my home network.
# Usage: borg_wrap.sh [-q|--quiet]
#
# Note: I run this as root, and have a /etc/sudoers.d/ file granting permissions.
#
# Note: I run this via crontab
# 0 */1 * * * sudo /usr/local/sbin/borg_wrap.sh -q
#
# Note: pruning:
# Keep last last 24h, one per day of last week, one per week of last month, one per month.
# borg prune -v --list --dry-run -d=7 -w=4 -m=-1 --keep-within=1d /path/to/repo

### Conf ### {{{
## Set Repo location
export BORG_REPO='ssh://demure@10.0.0.10:500/mnt/borg/doom-x270'

## Set the right ssh key
## Note, use with the following in the destination ssh authorized_keys
## command="borg serve --restrict-to-path /path/to/repo",no-pty,no-agent-forwarding,no-port-forwarding,no-X11-forwarding,no-user-rc ssh-rsa AAAAB3[...]
export BORG_RSH="ssh -i $HOME/.ssh/borg_id_ed25519"

## Pull Repo passwd
export BORG_PASSPHRASE="$(pass cli/borg 2>/dev/null)"

## Path to log file
Log_Path="$HOME/.config/borg/log"

## Temp file for log pruning
Log_tmp="/tmp/borg_log.tmp"

## Define remote MAC addr, for security check.
MAC_addr="24:5e:be:0d:b4:04"

## Define remote address, for testing.
Host="10.0.0.10"

## Define User for chmod fix (due to running with sudo)
User="demure"
### End Conf ### }}}

### Conf Check ### {{{
## Exit if BORG_REPO not specified.
if [ "${BORG_REPO}" = "" ]; then
    echo 'No repo specified.'
    exit 78
fi

## Exit if no log file specified.
if [ "${Log_Path}" = "" ]; then
    echo 'No log file specified.'
    exit 78
fi

## Exit if no router MAC specified.
if [ "${MAC_addr}" = "" ]; then
    echo 'No router MAC specified.'
    exit 78
fi

## Exit if storage address specified.
if [ "${Host}" = "" ]; then
    echo 'No storage address specified.'
    exit 78
fi

## Exit if no chown user specified.
if [ "${User}" = "" ]; then
    echo 'User for chown not set.'
    exit 78
fi
### End Conf Check ### }}}

### Startup Check ### {{{
    ### Augment Check ### {{{
    if [ $# -gt 1 ]; then
        echo 'Too many augments.'
        exit 1
    fi

    ## Initialize Variables
    Quiet=0                     ## Default, not quite
    VERBOSE='-v --stats -p'     ## Default, verbose output
    Prune=0                     ## Default, not prune mode
    DRY=''                      ## Default, not dry

    if [ ! -z "${1}" ]; then
        if [ "${1}" = '-q' ] || [ "${1}" = '--Quiet' ]; then
            VERBOSE=""
            Quiet=1
        elif [ "${1}" = '--prune' ]; then
            Prune=1
        elif [ "${1}" = '--dry-prune' ]; then
            Prune=1
            DRY='--dry-run'
        else
            echo 'Bad augments.'
            exit 1
        fi
    fi
    ### End Augment Check ### }}}

    ### Network Check ### {{{
    ## Check for host, then check if MAC addr matches
    ping -c 3 ${Host} 1>/dev/null
    if [ ! $? = 0 ]; then
        if [ ${Quiet} -eq 0 ]; then
            echo "Can't reach host."
        fi
        Error='NoPing'
      else
        check_mac="$(/bin/ip neigh | awk -v HOST=${Host} '$0~HOST{print $5}')"
        if [ ! "${MAC_addr}" = "${check_mac}" ]; then
            if [ ${Quiet} -eq 0 ]; then
                echo "Not on home network."
            fi
            Error='BadNet'
        fi
    fi
    ### End Network Check ### }}}

    ### Passwd Load Check ### {{{
    if [ -z "${BORG_PASSPHRASE}" ]; then
        if [ ${Quiet} -eq 0 ]; then
            echo "Password failed to load."
        fi
        Error='NoPass'
    fi
    ### End Passed Load Check ### }}}
### End Startup Check ### }}}

### Prune Mode ### {{{
## Note: prune mode shouln't need sudo
if [ "${Prune}" -eq 1 ]; then
    if [ ! -z ${Error} ]; then
        echo "Error: ${Error}"
        exit 1
    fi

    borg prune ${DRY} -v -s --list -d=7 -w=4 -m=-1 --keep-within=1d
    exit 0

fi
### End Prune Mode ### }}}

## If no errors, run.
## Note: Will need sudo if backing up system wide files
## A suggested naming convntion
## ::'{hostname}_{now:%Y-%m-%d_%H%M}' \
if [ -z $Error ]; then
    borg create ${VERBOSE} -C lz4 \
                ::'doom-x270_{now:%Y-%m-%d_%H%M}' \
                / \
                --exclude-caches \
                -e /dev \
                -e /media/ \
                -e /mnt/ \
                -e /proc \
                -e /run \
                -e /sys \
                -e /tmp \
                -e /var/cache \
                -e '/var/tmp/*' \
                -e '/var/db/*' \
                -e '/home/*/Downloads/' \
                -e '/home/*/temp/' \
                -e '/home/*/vault' \
                -e '/home/*/.config/chromium' \
                -e '/home/*/.local/share/mana/updates' \
                -e '*.cache' \
                -e '*.mail' \
                -e '*.thumbnails'

    ## Catch exit code
    Error=$?
fi

## Log pass/fail state
if [ ! $Error = "0" ]; then
    echo "$(date '+%F %T')\tfailed\t${Error}" | cat >> ${Log_Path}
  else
    echo "$(date '+%F %T')\tpassed" | cat >> ${Log_Path}
fi

## This prunes the log, leaving last 30 days.
## If no passed backups in 30 days, also leaves lasted passed entry.
## You can comment out to disable
awk -v cutoff="$(date -d"-30 days" +%F)" 'BEGIN{pchk=0} {a[i++]=$0} END{for(j=i-1;j>=0;j--) if(a[j] >= cutoff || pchk==0 && a[j] ~ /passed/){if(a[j] ~ /passed/){pchk=1}; b[k++]=a[j]};{for(l=k-1;l>=0;l--) print b[l]}}' ${Log_Path} > ${Log_tmp} && mv ${Log_tmp} ${Log_Path}

## Fix borg permissions so that things like `borg list` work without sudo
chown -R ${User}:${User} ${HOME}/.config/borg ${HOME}/.cache/borg
